#include "hal_PCF8563.h"

#define	PCF8563_DEV_ADDR	0xa2    // 设备地址
#define	PCF8563_REG_SECOND	0x02    // 存储地址(寄存器地址): 时间(秒)存储地址

#define  WRITE_BCD(val)  (val % 10) + ((val / 10) << 4)
#define  READ_BCD(val)   (val & 0x0f) + ((val & 0xf0) >> 4) * 10

#define EXIT_GPIO_PORT GPIO_P3
#define EXIT_GPIO_PIN GPIO_Pin_7
#define I2C_GPIO_PORT GPIO_P3
#define I2C_GPIO_PIN GPIO_Pin_2 | GPIO_Pin_3

/************************GPIO配置 **********************/
static void GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure; // 结构定义
    GPIO_InitStructure.Pin  = I2C_GPIO_PIN; // 指定要初始化的IO,
    GPIO_InitStructure.Mode = GPIO_OUT_OD;  // 指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
    GPIO_Inilize(I2C_GPIO_PORT, &GPIO_InitStructure); // 初始化
    GPIO_InitStructure.Pin  = EXIT_GPIO_PIN;// 指定要初始化的IO,
    GPIO_InitStructure.Mode = GPIO_PullUp; // 指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
    GPIO_Inilize(EXIT_GPIO_PORT, &GPIO_InitStructure); // 初始化
}

/****************  I2C初始化函数 *****************/
static void  I2C_Config(void)
{
    I2C_InitTypeDef I2C_InitStructure;
    I2C_InitStructure.I2C_Mode    = I2C_Mode_Master; // 主从选择   I2C_Mode_Master, I2C_Mode_Slave
    I2C_InitStructure.I2C_Enable  = ENABLE;          // I2C功能使能,   ENABLE, DISABLE
    I2C_InitStructure.I2C_MS_WDTA = DISABLE;         // 主机使能自动发送,  ENABLE, DISABLE
    I2C_InitStructure.I2C_Speed   = 13;              // 总线速度=Fosc/2/(Speed*2+4),  0~63     400k, 24M => 13
    I2C_Init(&I2C_InitStructure);
    NVIC_I2C_Init(I2C_Mode_Master, DISABLE, Priority_0); // 主从模式, I2C_Mode_Master, I2C_Mode_Slave; 中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
    I2C_SW(I2C_P33_P32); // I2C_P14_P15,I2C_P24_P25,I2C_P33_P32
}

/********************外部中断Exti配置 ********************/
static void	Exti_Config()
{
    EXTI_InitTypeDef	Exti_InitStructure;	//结构定义
    Exti_InitStructure.EXTI_Mode      = EXT_MODE_RiseFall;//中断模式,   EXT_MODE_RiseFall,EXT_MODE_Fall
    Ext_Inilize(EXT_INT3,&Exti_InitStructure);	//初始化
    NVIC_INT3_Init(ENABLE,Priority_0); //中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
}

// PCF8563初始化
void PCF8563_Init() {
    EA=1; // 使能全局中断开关
    EAXSFR(); /* 扩展寄存器访问使能 */
    GPIO_Config();
    I2C_Config();
    Exti_Config();
}

// 设置时间
void PCF8563_Set_Clock(Clock_t temp) {
    u8 p[7] = {0};

    // 秒的寄存器地址为： 0x02
    // 秒:  第0~3位记录个位，第4~6位记录十位
	p[0] = WRITE_BCD(temp.second);
    // 分: 第0~3位，保存个数，第4到6位，保存十位
	p[1] = WRITE_BCD(temp.minute);
    // 时：第0~3位，保存个数，第4到5位，保存十位
	p[2] = WRITE_BCD(temp.hour);
    // 日：第0~3位，保存个数，第4到5位，保存十位
	p[3] = WRITE_BCD(temp.day);
    // 周：第0~2位，保存个数
	p[4] = temp.weekday;
    // 月_世纪:  第0~3位记录个位，第4位记录十位，第7位为0，世纪数为20xx; 为1，世纪数为21xx
	p[5] = WRITE_BCD(temp.month);
	if(temp.year >= 2100) { // p[5] 的第7位为1
		p[5] |= (1 << 7);
	} else { // p[5] 的第7位为0
		p[5] &= ~(1 << 7);
	}
    // 年：第0~3位，保存个数，第4到7位，保存十位
	// 2025 => 25 => 2 和 5
	p[6] = WRITE_BCD(temp.year % 100);

    // 往rtc时钟芯片，写数据
    I2C_WriteNbyte(PCF8563_DEV_ADDR, PCF8563_REG_SECOND, p, 7);
}

// 获取时间
void PCF8563_Get_Clock(Clock_t *p) {
    u8 temp[7]  = {0}; // 接收数据
    u8 flag;
    // 读取rtc时钟芯片，数据, 读操作也是用写地址，内部会把写地址，变成读地址
    I2C_ReadNbyte(PCF8563_DEV_ADDR, PCF8563_REG_SECOND, temp, 7);
    // 秒的寄存器地址为： 0x02
    // 秒:  第0~3位记录个位，第4~6位记录十位
    p->second = READ_BCD(temp[0]); // 秒
    // 分: 第0~3位，保存个数，第4到6位，保存十位
    p->minute = READ_BCD(temp[1]); // 分
    // 时：第0~3位，保存个数，第4到5位，保存十位
    p->hour = READ_BCD(temp[2]); // 时
    // 日：第0~3位，保存个数，第4到5位，保存十位
    p->day = READ_BCD(temp[3]); // 日
    // 周：第0~2位，保存个数
    p->weekday = temp[4]; // 周
    // 月_世纪:  第0~3位记录个位，第4位记录十位，第7位为0，世纪数为20xx，为1，世纪数为21xx
    // 取出月的第7位的值
    flag = (temp[5] & (1 << 7)) >> 7; // 取出月的第7位的值
    // 给第7位置零
    temp[5] &= ~(1 << 7); // 给第7位置零
    // 取出月的第4到6位的值
    p->month = READ_BCD(temp[5]); // 月
    // 年：第0~3位，保存个数，第4到7位，保存十位
    p->year = READ_BCD(temp[6]); // 年
    if (flag == 1) { // 世纪数为21xx
        p->year += 2100;
    }else {
        p->year += 2000;
    }
}


void PCF8563_Set_Alarm(Alarm_t alarm){
    u8 p[4];
    // 分: 第0~3位,记录个数, 第4~6位记录十位, 第7位：置0启动, 置1禁用
    // 默认第7位为0
    p[0] = WRITE_BCD(alarm.minute); // 分Alarm_t.
    // 时: 第0~3位,记录个数, 第4~5位记录十位, 第7位：置0启动, 置1禁用
    p[1] = WRITE_BCD(alarm.hour); // 时
    // 日: 第0~3位,记录个数, 第4~5位记录十位, 第7位：置0启动, 置1禁用
    p[2] = WRITE_BCD(alarm.day); // 日
    // 周: 第0~2位,记录个数, 第7位：置0启动, 置1禁用
    p[3] = WRITE_BCD(alarm.weekday); // 周
    I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x09, p, 4);
}

// 启用闹钟
void PCF8563_Enable_Alarm(){
    u8 cfg;
    //===================2.2 闹钟开启 寄存器地址 0x01
    //a) 读原来的配置(不要乱改配置，只改自己的位，其它维持不变)
    I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);//不要省略这步，因为cfg没有值，需要先将p数组的值赋给cfg，再再原来的基础上改对应的位，再次写入
    //b) 在原来配置的基础上，清除标志位  第3位：置0清除标志位
    cfg &= ~(1 << 3);
    //c) 在原来配置基础上，启动闹钟，第1位：置1启动 
    cfg |= (1 << 1);
    //d) 重新写入配置
    I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}

// 禁用闹钟Alarm
void PCF8563_Disable_Alarm(){
    u8 cfg;
    //a) 读原来的配置(不要乱改配置，只改自己的位，其它维持不变)
    I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
    //b) 在原来配置的基础上，清除标志位  第3位：置0清除标志位
    cfg &= ~(1 << 3);
    //c) 在原来配置基础上，禁用闹钟，第1位：置0禁用
    cfg &= ~(1 << 1);
    //d) 重新写入配置
    I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}
// 清理闹钟标记
void PCF8563_Alarm_Clear_Flag(){
	u8 cfg;
    // ============================= 清除标志位，让闹钟能重复触发
    //a) 读原来的配置(不要乱改配置，只改自己的位，其它维持不变)
    I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
    //b) 在原来配置的基础上，清除标志位  第3位：置0清除标志位
    cfg &= ~(1 << 3);
    //d) 重新写入配置
    I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}


// 设置定时器，参数1：时钟频率 参数2：倒计时计算值，时间为：参数2/参数1
void PCF8563_Set_Timer(TimerFreq freq, u8 countdown){
    u8 p[2];
    //============1 定时器设置 寄存器地址 0x0e
    //a) 时钟频率
    // 0x00: 4.096 khz   0x01: 64 hz  0x02: 1hz  0x03: 1/60 hz
    // 第7位为0，定时器禁用，第七位为1，定时器启用
    p[0]=freq + 0x80; // 0x80: 定时器启用;
     //b) 计数值(0~255) ===》时间为： 计数值/时钟频率 
    p[1]=countdown; 
    //c) 写入配置
    I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x0e, p, 2);//时钟频率的寄存器地址为0x0e
}

// 启动定时器
void PCF8563_Enable_Timer(){
    u8 cfg;
    //===================2.2 定时器开启 寄存器地址 0x01
    //a) 读原来的配置(不要乱改配置，只改自己的位，其它维持不变)
    I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);//不要省略这步，因为cfg没有值，需要先将p数组的值赋给cfg，再再原来的基础上改对应的位，再次写入
    //b) 在原来配置的基础上，清除标志位  第2位：置0清除标志位
    cfg &= ~(1 << 2);
    //c) 在原来配置基础上，启动定时器，第0位：置1启动 
    cfg |= (1 << 0);
    //d) 重新写入配置
    I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}

// 禁用定时器
void PCF8563_Disable_Timer(){
    u8 cfg;
    //===================2.2 定时器开启 寄存器地址 0x01
    //a) 读原来的配置(不要乱改配置，只改自己的位，其它维持不变)
    I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);//不要省略这步，因为cfg没有值，需要先将p数组的值赋给cfg，再再原来的基础上改对应的位，再次写入
    //b) 在原来配置的基础上，清除标志位  第2位：置0清除标志位
    cfg &= ~(1 << 2);
    //c) 在原来配置基础上，禁用定时器，第0位：置0禁用
    cfg &= ~(1 << 0);
    //d) 重新写入配置
    I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}

// 清除定时器标志位
void PCF8563_Clear_Timer(){
    u8 cfg;
    //===================2.2 定时器开启 寄存器地址 0x01
    //a) 读原来的配置(不要乱改配置，只改自己的位，其它维持不变)
    I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);//不要省略这步，因为cfg没有值，需要先将p数组的值赋给cfg，再再原来的基础上改对应的位，再次写入
    //b) 在原来配置的基础上，清除标志位  第2位：置0清除标志位
    cfg &= ~(1 << 2);
    //d) 重新写入配置
    I2C_WriteNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
}  

// 外部中断3回调函数
// 该函数在中断发生时被调用，具体的中断处理逻辑在这里实现
void ext_int3_call() { // 外部中断3回调函数
    u8 cfg;
    // 读原来的配置
    I2C_ReadNbyte(PCF8563_DEV_ADDR, 0x01, &cfg, 1);
    //第3位置1：闹钟启动；第1位置1：闹钟中断触发
    if((cfg & 0x08)&&(cfg&0x02)) { // 0x08: 00001000 0x02: 00000010
        #if USE_ALARM_CALLBACK 
            PCF8563_on_alarm(); // 调用alarm回调函数
        #endif
        PCF8563_Alarm_Clear_Flag(); // 清除标志位  
    }
    //第2位置1：定时器启动；第0位置1：定时器中断触发
    if((cfg & 0x04)&&(cfg & 0x01)) { // 0x04: 00000100 0x01: 00000001)) { 
        #if USE_TIMER_CALLBACK 
            PCF8563_on_timer(); // 调用timer回调函数 
        #endif
        PCF8563_Clear_Timer(); // 清除标志位
    }

}